using System;
using ParserGenerator.Extensions;

namespace ParserGenerator.Input
{
    [Serializable]
    public abstract class SourceDocument
    {
        public abstract string Name { get; }
        public abstract string FullName { get; }

        internal SourceDocument()
        {
        }

        public abstract string TryGetSource();

        public virtual string GetSource()
        {
            string value = TryGetSource();
            if (value == null)
                throw new InvalidOperationException(string.Format(SR.SourceOfDocNotAvailable, Name));
            return value;
        }

        public virtual string TryGetSource(int begin, int end)
        {
            if (begin < 0)
                throw new ArgumentOutOfRangeException(nameof(begin), SR.BeginMustBeNonNegative);
            if (end < 0)
                throw new ArgumentOutOfRangeException(nameof(end), SR.EndMustBeNonNegative);
            if (begin > end)
                throw new ArgumentException(SR.BeginEndNotRange);
            string value = TryGetSource();
            if (value == null || begin > value.Length || end > value.Length)
                return null;
            return value.Substring(begin, end - begin);
        }

        public virtual string GetSource(int begin, int end)
        {
            if (begin < 0)
                throw new ArgumentOutOfRangeException(nameof(begin), SR.BeginMustBeNonNegative);
            if (end < 0)
                throw new ArgumentOutOfRangeException(nameof(end), SR.EndMustBeNonNegative);
            if (begin > end)
                throw new ArgumentException(SR.BeginEndNotRange);
            string value = GetSource();
            if (begin > value.Length)
                throw new ArgumentOutOfRangeException(nameof(begin), SR.BeginOutOfRange);
            if (end > value.Length)
                throw new ArgumentOutOfRangeException(nameof(end), SR.EndOutOfRange);
            return value.Substring(begin, end - begin);
        }

        public virtual string TryGetLine(int line)
        {
            if (line < 0)
                throw new ArgumentOutOfRangeException(nameof(line), SR.LineMustBeNonNegative);
            string value = TryGetSource();
            if (value == null)
                return null;
            return value.TryGetLine(line);
        }

        public virtual string GetLine(int line)
        {
            if (line < 0)
                throw new ArgumentOutOfRangeException(nameof(line), SR.LineMustBeNonNegative);
            string value = GetSource();
            return value.GetLine(line);
        }

        public virtual string TryGetLineAtCharIndex(int index)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException(nameof(index), SR.IndexMustBeNonNegative);
            string value = TryGetSource();
            if (value == null)
                return null;
            return value.TryGetLineAtCharIndex(index);
        }

        public virtual string GetLineAtCharIndex(int index)
        {
            if (index < 0)
                throw new ArgumentOutOfRangeException(nameof(index), SR.IndexMustBeNonNegative);
            string value = GetSource();
            return value.GetLineAtCharIndex(index);
        }
    }
}
